package andev.aandroidlib.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;

import andev.aandroidlib.listener.ATouch;

/**
 * Created by lzy1g on 2015/8/11.
 */
public class MatrixImageView extends ImageView {
    public Matrix matrix = new Matrix();
    private float[] values = new float[9];
    private GestureDetector gestureDetector;
    private DragListener dragListener;
    private SwipeListener mSwipeListener;
    private Bitmap mBitmap;
    private float mBitmapWidth, mBitmapHeight;
    private int viewLeft, viewRight, viewTop, viewBottom;
    private int viewWidth, viewHeight;
    private boolean bFitView = true;

    public MatrixImageView(Context context) {
        super(context);
        Init();
    }

    public MatrixImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        Init();
    }

    public void setDragListener(DragListener l) {
        this.dragListener = l;
    }

    private void Init() {
        setScaleType(ScaleType.MATRIX);
        setImageMatrix(matrix);

        setOnTouchListener(new ATouch() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                super.onTouch(v, event);
                switch (event.getAction() & event.getActionMasked()) {
                    case MotionEvent.ACTION_UP:
                        if (bFitView) {
                            fitView();
                        } else {
                            if (values[0] < 1.0f)
                                fitBitmap();
                            if (values[0] > 1.0f) {
                                float maxViewWidth = viewWidth * 2.0f, maxViewHeight = viewHeight * 2.0f;
                                if (mBitmapWidth * values[0] > maxViewWidth | mBitmapHeight * values[4] > maxViewHeight) {
                                    float postScale;
                                    //if image self is bigger than twice of screen
                                    if (mBitmapWidth > maxViewWidth || mBitmapHeight > maxViewHeight) {
                                        postScale = 0.8f;
                                    } else //else use twice of screen and calculate the postScale
                                    {
                                        if (maxViewHeight > maxViewWidth)
                                            postScale = maxViewWidth / (mBitmapWidth * values[0]);
                                        else
                                            postScale = maxViewHeight / (mBitmapHeight * values[4]);
                                    }
                                    scaleImage(postScale);
                                }
                            }
                        }
                        break;
                }
                return gestureDetector.onTouchEvent(event);
            }

            @Override
            public void move(View v, float xChange, float yChange) {
                moveImage(xChange, yChange);

                if (bFitView) {
                    if (values[2] + mBitmapWidth * values[0] <= viewWidth / 3 * 2)
                        mSwipeListener.swipeLeft();
                    if (values[2] >= viewWidth / 3) mSwipeListener.swipeRight();
                }
            }

            @Override
            public void zoom(View v, float scale) {
                if (values[0] < 0.8f)
                    return;
                if (values[0] > 1.0f) {
                    if (mBitmapWidth * values[0] > viewWidth * 3.0f || mBitmapHeight * values[4] > viewHeight * 3.0f)
                        return;
                }

                scaleImage(scale);
            }
        });
    }

    private void moveImage(float xChange, float yChange) {
        matrix.getValues(values);

        values[2] += xChange;
        values[5] += yChange;

        matrix.setValues(values);

        setImageMatrix(matrix);

        if (dragListener != null) {
            if (xChange < 0) dragListener.dragLeft();
            if (xChange > 0) dragListener.dragRight();
        }
    }

    private void scaleImage(float scale) {
        //to calculate the TranslateXY we need to get the before scaledWidthHeight value
        float beforeScaleWidth = mBitmapWidth * values[0];
        float beforeScaleHeight = mBitmapHeight * values[4];

        //MSCALE_X,MSCALE_Y,Here is to update the Bitmap ScaleXY
        values[0] *= scale;
        values[4] = values[0];

        float afterScaleWidth = mBitmapWidth * values[0];
        float afterScaleHeight = mBitmapHeight * values[4];

        //TranslateXY should add a value of half of AfterScaledXY-BeforeScaledXY if we want to scale stay at the position
        values[2] += (beforeScaleWidth - afterScaleWidth) / 2;
        values[5] += (beforeScaleHeight - afterScaleHeight) / 2;

        matrix.setValues(values);
        setImageMatrix(matrix);
    }

    public void setSwipeListener(final SwipeListener l) {
        mSwipeListener = l;
        gestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onDown(MotionEvent e) {
                return true;
            }

            @Override
            public boolean onDoubleTap(MotionEvent e) {
                toggleMatrix();
                return super.onDoubleTap(e);
            }

            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                if (Math.abs(velocityX) > Math.abs(velocityY)) {
                    if (velocityX > 0.8 && values[2] > viewLeft) {
                        l.swipeRight();
                    }
                    if (velocityX < -0.8 && values[2] + mBitmapWidth * values[0] < viewRight) {
                        l.swipeLeft();
                    }
                }
                return super.onFling(e1, e2, velocityX, velocityY);
            }
        });
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        super.setImageBitmap(bm);
        mBitmap = bm;
        mBitmapWidth = mBitmap.getWidth();
        mBitmapHeight = mBitmap.getHeight();
        fitView();
    }

    public void changeImageBitmap(Bitmap bm, boolean bNext) {
        TranslateAnimation translateAnimation;
        if (bNext) {
            translateAnimation = new TranslateAnimation(viewRight, viewLeft, viewTop, viewTop);
        } else {
            translateAnimation = new TranslateAnimation(-viewRight, viewLeft, viewTop, viewTop);
        }
        translateAnimation.setDuration(120);
        startAnimation(translateAnimation);
        setImageBitmap(bm);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        viewLeft = left;
        viewRight = right;
        viewTop = top;
        viewBottom = bottom;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        viewWidth = w;
        viewHeight = h;
        toggleMatrix();
    }

    private void toggleMatrix() {
        if (bFitView) {
            fitView();
            bFitView = !bFitView;
        } else {
            fitBitmap();
            bFitView = !bFitView;
        }
    }

    private void fitView() {
        RectF whole = new RectF(0, 0, viewWidth, viewHeight);
        RectF image = new RectF(0, 0, mBitmapWidth, mBitmapHeight);
        matrix.setRectToRect(image, whole, Matrix.ScaleToFit.CENTER);
        setImageMatrix(matrix);
    }

    private void fitBitmap() {
        matrix.setScale(1, 1);
        float dx = (viewWidth - mBitmapWidth) / 2;
        float dy = (viewHeight - mBitmapHeight) / 2;
        matrix.setTranslate(dx, dy);
        setImageMatrix(matrix);
    }

    public interface DragListener {
        void dragLeft();

        void dragRight();
    }

    public interface SwipeListener {
        void swipeLeft();

        void swipeRight();
    }

}
